"""
文件上传服务
"""
import os
import uuid
import shutil
from typing import List, Optional
from fastapi import UploadFile, HTTPException, status
from pathlib import Path
import mimetypes
import os
from config import settings


class FileUploadService:
    """文件上传服务"""
    
    # 允许的图片类型
    ALLOWED_IMAGE_TYPES = {
        'image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp'
    }
    
    # 允许的附件类型
    ALLOWED_ATTACHMENT_TYPES = {
        # PDF文件
        'application/pdf',
        # Word文档
        'application/msword',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        # Excel文档
        'application/vnd.ms-excel',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        # PowerPoint文档
        'application/vnd.ms-powerpoint',
        'application/vnd.openxmlformats-officedocument.presentationml.presentation',
        # 文本文件
        'text/plain',
        'text/csv',
        # 压缩文件
        'application/zip',
        'application/x-zip-compressed',
        'application/x-rar-compressed',
        'application/x-7z-compressed',
        # 图片文件
        'image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp', 'image/bmp',
        # 其他常见类型
        'application/octet-stream',  # 通用二进制文件
    }
    
    # 允许的数据源文件类型
    ALLOWED_DATASOURCE_TYPES = {
        'text/csv',
        'application/vnd.ms-excel',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    }
    
    @staticmethod
    def _get_user_upload_dir(user_id: int, file_type: str) -> Path:
        """获取用户上传目录"""
        # 确保基础目录存在
        base_dir = Path(settings.upload_dir).resolve()
        if not base_dir.exists():
            base_dir.mkdir(parents=True, exist_ok=True)

        # 创建用户目录
        user_dir = base_dir / f"user_{user_id}" / file_type
        user_dir.mkdir(parents=True, exist_ok=True)
        return user_dir
    
    @staticmethod
    def _generate_filename(original_filename: str) -> str:
        """生成唯一文件名"""
        # 获取文件扩展名
        file_ext = Path(original_filename).suffix.lower()
        # 生成UUID作为文件名
        unique_id = str(uuid.uuid4()).replace('-', '')
        return f"{unique_id}{file_ext}"
    
    @staticmethod
    def _validate_file_size(file: UploadFile) -> None:
        """验证文件大小"""
        # 如果无法获取文件大小，跳过大小检查
        if file.size is None:
            return

        if file.size > settings.max_file_size:
            raise HTTPException(
                status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
                detail=f"文件大小超过限制 ({settings.max_file_size / 1024 / 1024:.1f}MB)"
            )
    
    @staticmethod
    def _validate_file_type(file: UploadFile, allowed_types: set) -> None:
        """验证文件类型"""
        content_type = file.content_type
        print(f"文件类型验证: {content_type}")
        print(f"允许的类型: {allowed_types}")

        if content_type not in allowed_types:
            print(f"文件类型不被允许: {content_type}")
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"不支持的文件类型: {content_type}。支持的类型: {', '.join(sorted(allowed_types))}"
            )
        print(f"文件类型验证通过: {content_type}")
    
    @staticmethod
    async def upload_image(file: UploadFile, user_id: int) -> dict:
        """上传图片文件"""
        try:
            # 验证文件
            FileUploadService._validate_file_size(file)
            FileUploadService._validate_file_type(file, FileUploadService.ALLOWED_IMAGE_TYPES)
            
            # 获取上传目录
            upload_dir = FileUploadService._get_user_upload_dir(user_id, "images")
            
            # 生成文件名
            filename = FileUploadService._generate_filename(file.filename)
            file_path = upload_dir / filename
            
            # 保存文件
            try:
                with open(file_path, "wb") as buffer:
                    # 重置文件指针到开始位置
                    file.file.seek(0)
                    shutil.copyfileobj(file.file, buffer)
            except Exception as save_error:
                raise HTTPException(
                    status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                    detail=f"文件保存失败: {str(save_error)}"
                )
            
            # 生成访问URL
            relative_path = f"uploads/user_{user_id}/images/{filename}"
            
            # 获取实际文件大小
            actual_size = file.size
            if actual_size is None:
                try:
                    actual_size = os.path.getsize(file_path)
                except:
                    actual_size = 0

            return {
                "filename": filename,
                "original_filename": file.filename,
                "file_path": str(file_path),
                "relative_path": relative_path,
                "url": f"/{relative_path}",
                "size": actual_size,
                "content_type": file.content_type
            }
            
        except Exception as e:
            if isinstance(e, HTTPException):
                raise e
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"文件上传失败: {str(e)}"
            )
    
    @staticmethod
    async def upload_attachment(file: UploadFile, user_id: int) -> dict:
        """上传附件文件"""
        try:
            print(f"FileUploadService.upload_attachment 开始: {file.filename}")

            # 验证文件
            print(f"验证文件大小: {file.size}")
            FileUploadService._validate_file_size(file)

            print(f"验证文件类型: {file.content_type}")
            FileUploadService._validate_file_type(file, FileUploadService.ALLOWED_ATTACHMENT_TYPES)
            
            # 获取上传目录
            print(f"获取上传目录: user_{user_id}/attachments")
            upload_dir = FileUploadService._get_user_upload_dir(user_id, "attachments")
            print(f"上传目录: {upload_dir}")

            # 生成文件名
            filename = FileUploadService._generate_filename(file.filename)
            file_path = upload_dir / filename
            print(f"文件路径: {file_path}")
            
            # 保存文件
            try:
                with open(file_path, "wb") as buffer:
                    # 重置文件指针到开始位置
                    file.file.seek(0)
                    shutil.copyfileobj(file.file, buffer)
            except Exception as save_error:
                raise HTTPException(
                    status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                    detail=f"文件保存失败: {str(save_error)}"
                )

            # 生成访问URL
            relative_path = f"uploads/user_{user_id}/attachments/{filename}"

            # 获取实际文件大小
            actual_size = file.size
            if actual_size is None:
                try:
                    actual_size = os.path.getsize(file_path)
                except:
                    actual_size = 0

            return {
                "filename": filename,
                "original_filename": file.filename,
                "file_path": str(file_path),
                "relative_path": relative_path,
                "url": f"/{relative_path}",
                "size": actual_size,
                "content_type": file.content_type
            }
            
        except Exception as e:
            if isinstance(e, HTTPException):
                raise e
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"附件上传失败: {str(e)}"
            )
    
    @staticmethod
    async def upload_datasource(file: UploadFile, user_id: int) -> dict:
        """上传数据源文件"""
        try:
            # 验证文件
            FileUploadService._validate_file_size(file)
            FileUploadService._validate_file_type(file, FileUploadService.ALLOWED_DATASOURCE_TYPES)
            
            # 获取上传目录
            upload_dir = FileUploadService._get_user_upload_dir(user_id, "datasources")
            
            # 生成文件名
            filename = FileUploadService._generate_filename(file.filename)
            file_path = upload_dir / filename
            
            # 保存文件
            with open(file_path, "wb") as buffer:
                shutil.copyfileobj(file.file, buffer)
            
            # 生成访问URL
            relative_path = f"uploads/user_{user_id}/datasources/{filename}"
            
            return {
                "filename": filename,
                "original_filename": file.filename,
                "file_path": str(file_path),
                "relative_path": relative_path,
                "url": f"/{relative_path}",
                "size": file.size,
                "content_type": file.content_type
            }
            
        except Exception as e:
            if isinstance(e, HTTPException):
                raise e
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"数据源文件上传失败: {str(e)}"
            )
    
    @staticmethod
    def delete_file(file_path: str) -> bool:
        """删除文件"""
        try:
            if os.path.exists(file_path):
                os.remove(file_path)
                return True
            return False
        except Exception as e:
            print(f"删除文件失败: {str(e)}")
            return False
    
    @staticmethod
    def get_file_info(file_path: str) -> Optional[dict]:
        """获取文件信息"""
        try:
            if not os.path.exists(file_path):
                return None
            
            stat = os.stat(file_path)
            filename = os.path.basename(file_path)
            content_type, _ = mimetypes.guess_type(file_path)
            
            return {
                "filename": filename,
                "size": stat.st_size,
                "content_type": content_type,
                "created_time": stat.st_ctime,
                "modified_time": stat.st_mtime
            }
        except Exception as e:
            print(f"获取文件信息失败: {str(e)}")
            return None
